#include <linux/module.h>
#include <linux/init.h>
#include <linux/fs.h>
#include <linux/uaccess.h>
#include <linux/wait.h>
#include <linux/poll.h>

#define DEV_NAME "mypoll"
#define BUF_LEN 128

static int major;
static char data_buf[BUF_LEN];
static int data_available = 0;

static DECLARE_WAIT_QUEUE_HEAD(my_waitqueue);

// open
static int my_open(struct inode *inode, struct file *file) {
    return 0;
}

// release
static int my_release(struct inode *inode, struct file *file) {
    return 0;
}

// read
static ssize_t my_read(struct file *file, char __user *buf, size_t size, loff_t *ppos) {
    if (!data_available) {
        if (file->f_flags & O_NONBLOCK)
            return -EAGAIN;
        wait_event_interruptible(my_waitqueue, data_available != 0);
    }

    copy_to_user(buf, data_buf, BUF_LEN);
    data_available = 0;

    return BUF_LEN;
}

// write
static ssize_t my_write(struct file *file, const char __user *buf, size_t size, loff_t *ppos) {
    copy_from_user(data_buf, buf, BUF_LEN);
    data_available = 1;

    wake_up_interruptible(&my_waitqueue);  // 唤醒阻塞在poll/select的用户进程
    return size;
}

// poll
static unsigned int my_poll(struct file *file, poll_table *wait) {
    unsigned int mask = 0;

    poll_wait(file, &my_waitqueue, wait);  // 挂入等待队列

    if (data_available)
        mask |= POLLIN | POLLRDNORM;

    return mask;
}

static struct file_operations my_fops = {
    .owner = THIS_MODULE,
    .open = my_open,
    .release = my_release,
    .read = my_read,
    .write = my_write,
    .poll = my_poll,
};

static int __init my_init(void) {
    major = register_chrdev(0, DEV_NAME, &my_fops);
    printk(KERN_INFO "mypoll device registered. major = %d\n", major);
    return 0;
}

static void __exit my_exit(void) {
    unregister_chrdev(major, DEV_NAME);
    printk(KERN_INFO "mypoll device unregistered.\n");
}

module_init(my_init);
module_exit(my_exit);

MODULE_LICENSE("GPL");
MODULE_DESCRIPTION("poll/select test driver");